package com.jackstraw.jack_base.datasource.mybatis.base.service.impl;

import com.jackstraw.jack_base.datasource.database.parten.base.entity.page.PageData;
import com.jackstraw.jack_base.datasource.mybatis.base.dao.MyBatisDao;
import com.jackstraw.jack_base.datasource.mybatis.base.entity.po.MyBatisPo;
import com.jackstraw.jack_base.datasource.mybatis.base.entity.po.PoExample;
import com.jackstraw.jack_base.datasource.mybatis.base.service.MyBatisService;
import com.jackstraw.jack_base.datasource.mybatis.exception.BadMybatisException;
import com.jackstraw.jack_base.util.CollectionUtil;
import com.jackstraw.jack_base.util.ObjectUtil;
import com.jackstraw.jack_base.util.StringUtil;
import com.jackstraw.jack_base.util.entity.DefaultConstants;
import com.jackstraw.jack_base.util.function.ReflectionUtil;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Mybatis基本Service实现抽象类
 *
 * @param <DAO> 数据库持久层
 * @param <PO>  实体类
 * @param <BO>  BO
 * @param <ID>  主键类
 * @param <PE>  PoExample
 */
public class MyBatisServiceImpl<DAO extends MyBatisDao<PO, ID, PE>,
        PO extends ID, BO extends PO,
        ID extends MyBatisPo<PO>, PE extends PoExample>
        implements MyBatisService<DAO, PO, BO, ID, PE> {


    /**
     * 数据持久层
     */
    @Autowired
    protected DAO dao;

    /**
     * 实体类Class
     */
    private final Class<PO> poClass = currentPOClass();

    /**
     * 数据持久层Class
     */
    private final Class<DAO> daoClass = currentDAOClass();

    /**
     * PoExample Class
     */
    private final Class<PE> peClass = currentPEClass();

    /**
     * 业务操作类Class
     */
    private final Class<BO> boClass = currentBOClass();

    /**
     * 主键类型
     */
    private final Class<ID> idClass = currentIDClass();

    /**
     * 字段名称和数据库字段名称对应关系
     */
    private final Map<String, String> fieldColumnNameMap = currentFieldColumnNameMap();

    private Map<String, String> currentFieldColumnNameMap() {
        try {
            Method fieldColumnNameMethod = this.getIdClass().getMethod("fieldColumnNameMap");
            PO po = this.newPO();
            Map<String, String> fieldColumnNameMap = (Map<String, String>) fieldColumnNameMethod.invoke(po);
            return fieldColumnNameMap;
        } catch (Exception e) {
            throw new BadMybatisException("001", "获取字符对应关系异常", e);
        }

    }

    private Class<PE> currentPEClass() {
        return ReflectionUtil.getClassGenricType(this.getClass(), 4);
    }

    @Override
    public DAO getDao() {
        return this.dao;
    }

    @Override
    public Class<PO> getPoClass() {
        return this.poClass;
    }

    @Override
    public Class<BO> getBoClass() {
        return this.boClass;
    }

    @Override
    public Class<DAO> getDaoClass() {
        return this.daoClass;
    }

    @Override
    public Class<ID> getIdClass() {
        return this.idClass;
    }

    @Override
    public Class<PE> getPeClass() {
        return peClass;
    }

    /**
     * 根据传入的Map设置查询条件以及排序、分页
     * <p>
     * 查询条件未设置，需要重写该方法
     *
     * @param params
     * @return
     */

    protected PE pe(Map<String, Object> params) {
        PE pe = newPE();
        if (CollectionUtil.MapUtil.isEmpty(params)) {
            return pe;
        }
        //分页参数
        if (params.get(DefaultConstants.PAGE_NUM) != null) {
            Integer pageNum = Integer.valueOf((Integer) params.get(DefaultConstants.PAGE_NUM));
            pe.setPageNum(pageNum);
        }
        if (params.get(DefaultConstants.PAGE_SIZE) != null) {
            Integer pageSize = Integer.valueOf((Integer) params.get(DefaultConstants.PAGE_SIZE));
            pe.setPageSize(pageSize);
        }

        String orderField = null;
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(params.get(DefaultConstants.ORDER_FIELD))) {
            orderField = (String) params.get(DefaultConstants.ORDER_FIELD);
        }

        Map<String, String> orderMap = null;
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(params.get(DefaultConstants.ORDER))) {
            orderMap = (Map<String, String>) params.get(DefaultConstants.ORDER);
        }

        if (StringUtil.StringUtilsBylang3.isNotEmpty(orderField)) {
            StringBuffer orderFieldSB = new StringBuffer();
            if (CollectionUtil.MapUtil.isEmpty(orderMap)) {
                orderMap = new HashMap<>();
            }
            Map<String, String> finalOrderMap = orderMap;
            List<String> orderFieldList = Arrays.asList(orderField.split(","));

            String lastField = orderFieldList.stream().reduce((first, second) -> second).orElse("no last element");
            orderFieldList.stream().forEach(field -> {
                String order = finalOrderMap.get(field);
                orderFieldSB.append(fieldColumnNameMap.get(field)).append(" ");
                if (StringUtil.StringUtilsBylang3.isNotEmpty(order)) {
                    orderFieldSB.append(order);
                }
                if (!field.equals(lastField)) {
                    orderFieldSB.append(",");
                }
            });
            orderField = orderFieldSB.toString();
            pe.setOrderByClause(orderField);
        }

        // TODO 条件未设置,需要重写该方法进行条件设置
        pe.addKey(params);

        return pe;
    }

    @Override
    public PageData<BO> page(Map<String, Object> params) {
        PE pe = pe(params);
        Long count = this.getDao().countByExample(pe);
        List<BO> boList = toBo(this.getDao().selectByExample(pe));

        PageData<BO> pageData = new PageData<>(boList, count);
        pageData.setPageNum(pe.getPageNum());
        pageData.setPageSize(pe.getPageSize());
        return pageData;
    }

    @Override
    public List<BO> list(Map<String, Object> params) {
        return toBo(this.getDao().selectByExample(pe(params)));
    }

    @Override
    public BO get(ID id) {
        return toBo(this.getDao().selectByPrimaryKey(id));
    }

    @Override
    public List<BO> get(Collection<ID> idS) {
        return toBo(this.getDao().selectMoreByPrimaryKey(idS));
    }

    @Override
    public void insert(BO bo) {
        this.getDao().insertSelective(toPo(bo));
    }

    @Override
    public List<BO> insertBatch(List<BO> boS) {
        List<PO> pos = toPo(boS);
        if (CollectionUtil.isEmpty(pos)) {
            return boS;
        }
        for (int i = 0; i < pos.size(); i += BATCH_SIZE) {
            int batchSize = BATCH_SIZE;
            if (i + BATCH_SIZE > pos.size()) {
                batchSize = pos.size() - i;
            }
            List<PO> posSubList = pos.subList(i, batchSize);
            this.getDao().insertBatch(posSubList);
        }
        return toBo(pos);
    }

    @Override
    public void update(BO bo) {
        this.getDao().updateByPrimaryKeySelective(bo);
    }

    @Override
    public void updateBatch(List<BO> boS) {
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(boS)) {
            return;
        }
        boS.stream().forEach(bo -> update(bo));
    }

    @Override
    public void delete(ID id) {
        this.getDao().deleteByPrimaryKey(id);
    }

    @Override
    public void deleteBatch(List<ID> idS) {
        if (CollectionUtil.isEmpty(idS)) {
            return;
        }
        for (int i = 0; i < idS.size(); i += BATCH_SIZE) {
            int batchSize = BATCH_SIZE;
            if (i + BATCH_SIZE > idS.size()) {
                batchSize = idS.size() - i;
            }
            List<ID> ids = idS.subList(i, batchSize);
            this.getDao().deleteBatchByPrimaryKey(ids);
        }
    }
}
